package com.foreverwin.jzymes.production.program.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.foreverwin.jzymes.common.constant.Constants;
import com.foreverwin.jzymes.common.service.IFieldGroupAdviceService;
import com.foreverwin.jzymes.common.service.IItemGroupAdviceService;
import com.foreverwin.jzymes.common.service.IResourceAdviceService;
import com.foreverwin.jzymes.common.service.ISiteAdviceService;
import com.foreverwin.jzymes.common.util.BOHandleUtil;
import com.foreverwin.jzymes.common.util.CommonMethods;
import com.foreverwin.jzymes.framework.exception.BusinessException;
import com.foreverwin.jzymes.framework.util.FTPUtil;
import com.foreverwin.jzymes.framework.util.I18nUtil;
import com.foreverwin.jzymes.framework.util.ZipUtil;
import com.foreverwin.jzymes.production.program.domain.CncProgram;
import com.foreverwin.jzymes.production.program.domain.CncProgramItem;
import com.foreverwin.jzymes.production.program.domain.ProgramActionLog;
import com.foreverwin.jzymes.production.program.enums.ProgramAction;
import com.foreverwin.jzymes.production.program.mapper.CncProgramItemMapper;
import com.foreverwin.jzymes.production.program.service.ICncProgramAdviceService;
import com.foreverwin.jzymes.production.program.service.ICncProgramItemService;
import com.foreverwin.jzymes.production.program.service.ICncProgramService;
import com.foreverwin.jzymes.production.program.service.IProgramActionLogService;
import org.apache.activemq.command.ActiveMQTopic;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;

@Service
public class CncProgramAdviceServiceImpl implements ICncProgramAdviceService {

    @Autowired
    FTPUtil ftpUtil;
    @Autowired
    ICncProgramService cncProgramService;
    @Autowired
    ISiteAdviceService siteAdviceService;
    @Autowired
    IItemGroupAdviceService itemGroupAdviceService;
    @Autowired
    IFieldGroupAdviceService fieldGroupAdviceService;
    @Autowired
    ICncProgramItemService cncProgramItemService;
    @Autowired
    IResourceAdviceService resourceAdviceService;
    @Autowired
    IProgramActionLogService programActionLogService;
    @Autowired
    CncProgramItemMapper cncProgramItemMapper;
    @Autowired
    JmsMessagingTemplate jmsTemplate;

    /**
     * 保存机种程式
     *
     * @param cncProgram
     * @param cncProgramItems
     */
    @Transactional( propagation = Propagation.REQUIRED, rollbackFor = Exception.class )
    public void saveCncProgram( CncProgram cncProgram, List<CncProgramItem> cncProgramItems ){

        /**检查字段*/
        checkFieldsForCncProgram( cncProgram );

        /**程式当前版本*/
        if( "true".equals( cncProgram.getCurrentRevision() ) ){
            CncProgram currentRevCncProgram = findCurrentRevCncProgram( cncProgram.getSite(), cncProgram.getMachineType(), cncProgram.getCncType() );
            if( currentRevCncProgram != null ){
                CncProgram updateParam = new CncProgram();
                updateParam.setHandle( currentRevCncProgram.getHandle() );
                updateParam.setCurrentRevision( "false" );
                cncProgramService.updateCncProgram( updateParam );
            }
        }

        /**保存机种程式*/
        CncProgram _cncProgram = null;
        _cncProgram = findCncProgram( cncProgram.getSite(), cncProgram.getMachineType(), cncProgram.getCncType(), cncProgram.getPrgmCode(), cncProgram.getRevision(), cncProgram.getSubSeq() );
        if( _cncProgram == null ){
            cncProgram.setHandle( BOHandleUtil.buildCncProgramHandle( cncProgram.getSite() , cncProgram.getMachineType(), cncProgram.getCncType(), cncProgram.getPrgmCode(), cncProgram.getRevision(), cncProgram.getSubSeq() ) );
            cncProgram.setCreatedDateTime( new Date() );
            cncProgram.setCreateUserBo( CommonMethods.getUserBo() );
            cncProgramService.insertCncProgram( cncProgram );
        }else{
            cncProgram.setHandle( _cncProgram.getHandle() );
            cncProgram.setCreatedDateTime( _cncProgram.getCreatedDateTime() );
            cncProgram.setUpdatedDateTime( new Date() );
            cncProgram.setUpdateUserBo( CommonMethods.getUserBo() );
            cncProgramService.updateCncProgram( cncProgram );
        }

        CncProgramItem queryParam = new CncProgramItem();
        queryParam.setCncPrgmBo( cncProgram.getHandle() );
        List<CncProgramItem> toolBillItemList = cncProgramItemService.selectCncProgramItemList( queryParam );
        for( CncProgramItem cncProgramItem : toolBillItemList ){
            cncProgramItemMapper.deleteCncProgramItemById( cncProgramItem.getHandle() );
        }

        /**保存机种程式清单明细*/
        for( CncProgramItem cncProgramItem : cncProgramItems ){
            /**检查字段*/
            checkFieldsForCncProgramItem( cncProgramItem );

            cncProgramItem.setHandle( BOHandleUtil.buildCncProgramItemHandle( cncProgram.getHandle(), cncProgramItem.getPrgmCode() ) );
            cncProgramItem.setCncPrgmBo( cncProgram.getHandle() );
            if( _cncProgram == null ){
                cncProgramItem.setType( "" );
            }
            cncProgramItemService.insertCncProgramItem( cncProgramItem );
        }

        /**压缩程式文件*/
        packCncProgram( cncProgram, cncProgramItems );
    }

    /**
     * 保存时生成程式压缩文件
     *
     * @param cncProgram
     * @param cncProgramItems
     */
    public void packCncProgram( CncProgram cncProgram, List<CncProgramItem> cncProgramItems  ){
        String machineType = cncProgram.getMachineType();
        String cncType = cncProgram.getCncType();
        String prgmCode = cncProgram.getPrgmCode();
        String revision = cncProgram.getRevision();
        String subSeq = cncProgram.getSubSeq();
        String remotePath = machineType + "-" + cncType + File.separator + prgmCode + "-" + revision + "-" + subSeq;

        File zipFile = zipProgramItem( cncProgram, cncProgramItems );
        try{
            ftpUtil.upload( remotePath, zipFile  );
        } catch (Exception e) {
            throw BusinessException.build( e.getMessage() );
        }finally {
            zipFile.delete();
        }

    }

    public void checkFieldsForCncProgram( CncProgram cncProgram ){
        /**检查站点*/
        siteAdviceService.existSite( cncProgram.getSite() );
        /**检查机种*/
        itemGroupAdviceService.existItemGroup( cncProgram.getSite(), cncProgram.getMachineType() );
        /**检查夹位*/
        fieldGroupAdviceService.existFieldGroup( cncProgram.getSite() , Constants.FIELD_GROUP_CncType , cncProgram.getCncType() );
        /**检查刀具清单版本*/
        Assert.hasText( cncProgram.getToolBillBo() , I18nUtil.getI18nText( "production.cnctool.toolBillRevision" ) );
        /**检查程式编号*/
        Assert.hasText( cncProgram.getPrgmCode(), I18nUtil.getI18nText("production.cncProgram.prgmCodeNotBlank") );
        /**检查版本*/
        Assert.hasText( cncProgram.getRevision(), I18nUtil.getI18nText("production.cncProgram.revisionNotBlank") );
        /**检查版次*/
        Assert.hasText( cncProgram.getSubSeq(), I18nUtil.getI18nText("production.cncProgram.subSeqNotBlank") );
    }

    public void checkFieldsForCncProgramItem( CncProgramItem cncProgramItem ){
        /**检查子程式编号*/
        Assert.hasText( cncProgramItem.getPrgmCode(), I18nUtil.getI18nText( "production.cncProgram.prgmCodeNotBlank" ) );
    }

    /**
     * 解析程式包
     *
     * @param file
     * @param machineType
     * @param cncType
     * @param prgmCode
     * @param revision
     * @param subSeq
     * @return
     * @throws Exception
     */
    public List<Map<String,String>> uploadProgram(MultipartFile file, String machineType, String cncType, String prgmCode, String revision, String subSeq ){

        /**检查机种*/
        Assert.hasText( machineType, I18nUtil.getI18nText("common.machineTypeNotBlank") );
        /**检查夹位*/
        Assert.hasText( cncType, I18nUtil.getI18nText("common.machineTypeNotBlank") );
        /**检查程式编号*/
        Assert.hasText( prgmCode, I18nUtil.getI18nText("production.cncProgram.prgmCodeNotBlank") );
        /**检查版本*/
        Assert.hasText( revision, I18nUtil.getI18nText("production.cncProgram.revisionNotBlank") );
        /**检查版次*/
        Assert.hasText( subSeq, I18nUtil.getI18nText("production.cncProgram.subSeqNotBlank") );

        String filePath = prgmCode + "-" + revision + "-" + subSeq + File.separator + file.getOriginalFilename().substring( 0, file.getOriginalFilename().indexOf( "." ) );

        String uploadDir = ftpUtil.getUploadDir();
        File tempFile = new File( uploadDir + File.separator + filePath );
        tempFile.mkdirs();

        List<Map<String,String>> fileList = new ArrayList<>();

        try {
            File _file = new File( tempFile.getPath() + File.separator + file.getOriginalFilename() );
            file.transferTo( _file );
            ZipUtil.unZip( _file, tempFile.getPath() );

            _file.delete();

            String remotePath = machineType + "-" + cncType + File.separator + prgmCode + "-" + revision + "-" + subSeq;
            //ftpUtil.deleteFile( remotePath );

            String remoteRootPath = machineType + "-" + cncType;
            ftpUtil.upload( remoteRootPath, tempFile.getParentFile() );

            fileList = buildUploadFileList( tempFile, remotePath );

        }catch( Exception e ){
            throw BusinessException.build( e.getMessage() );
        }finally {
            try {
                FileUtils.deleteDirectory( tempFile.getParentFile() );
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return fileList;
    }

    /**
     * 解析上传程式包
     *
     * @param fileItem
     * @param req
     * @return
     */
    public List<Map<String,String>> uploadProgram( FileItem fileItem, HttpServletRequest req ){

        String machineType = req.getParameter( "machineType" );
        String cncType = req.getParameter( "cncType" );
        String prgmCode = req.getParameter( "prgmCode" );
        String revision = req.getParameter( "revision" );
        String subSeq = req.getParameter( "subSeq" );

        /**检查机种*/
        Assert.hasText( machineType, I18nUtil.getI18nText("common.machineTypeNotBlank") );
        /**检查夹位*/
        Assert.hasText( cncType, I18nUtil.getI18nText("common.machineTypeNotBlank") );
        /**检查程式编号*/
        Assert.hasText( prgmCode, I18nUtil.getI18nText("production.cncProgram.prgmCodeNotBlank") );
        /**检查版本*/
        Assert.hasText( revision, I18nUtil.getI18nText("production.cncProgram.revisionNotBlank") );
        /**检查版次*/
        Assert.hasText( subSeq, I18nUtil.getI18nText("production.cncProgram.subSeqNotBlank") );

        String filePath = prgmCode + "-" + revision + "-" + subSeq + File.separator + fileItem.getName().substring( 0, fileItem.getName().indexOf( "." ) );

        String uploadDir = ftpUtil.getUploadDir();
        File tempFile = new File( uploadDir + File.separator + filePath );
        tempFile.mkdirs();

        List<Map<String,String>> fileList = new ArrayList<>();

        try {
            File _file = new File( tempFile.getPath() + File.separator + fileItem.getName() );
            FileOutputStream fos = new FileOutputStream( _file );
            IOUtils.copy( fileItem.getInputStream(), fos );
            fos.close();

            ZipUtil.unZip( _file, tempFile.getPath() );
            _file.delete();

            String remotePath = machineType + "-" + cncType + File.separator + prgmCode + "-" + revision + "-" + subSeq;
            //ftpUtil.deleteFile( remotePath );

            String remoteRootPath = machineType + "-" + cncType;
            ftpUtil.upload( remoteRootPath, tempFile.getParentFile() );

            fileList = buildUploadFileList( tempFile, remotePath );

        }catch( Exception e ){
            throw BusinessException.build( e.getMessage() );
        }finally {
            try {
                FileUtils.deleteDirectory( tempFile.getParentFile() );
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return fileList;
    }

    /**
     * 上传程式到cnc
     *
     * @param site
     * @param resrce
     * @param machineType
     * @param cncType
     * @param prgmCode
     * @param revision
     * @param subSeq
     * @param programFile
     */
    public void uploadProgramToCnc( String site, String resrce, String machineType, String cncType, String prgmCode, String revision, String subSeq, String programFile ){
        /**检查站点*/
        siteAdviceService.existSite( site );
        /**检查设备编号*/
        Assert.hasText( resrce, I18nUtil.getI18nText("common.resourceNotBlank") );
        //resourceAdviceService.existResource( site, resrce );
        /**检查机种*/
        itemGroupAdviceService.existItemGroup( site, machineType );
        /**检查夹位*/
        fieldGroupAdviceService.existFieldGroup( site, Constants.FIELD_GROUP_CncType , cncType );
        /**检查程式编号*/
        Assert.hasText( prgmCode, I18nUtil.getI18nText("production.cncProgram.prgmCodeNotBlank") );
        /**检查版本*/
        Assert.hasText( revision, I18nUtil.getI18nText("production.cncProgram.revisionNotBlank") );
        /**检查版次*/
        Assert.hasText( subSeq, I18nUtil.getI18nText("production.cncProgram.subSeqNotBlank") );
        /**检查程序*/
        Assert.hasText( programFile, I18nUtil.getI18nText("production.cncProgram.programFileNotBlank") );

        /**保存程式上传CNC操作记录*/
        String[] resources = resrce.split( "," );
        for( String resource : resources ){
            ProgramActionLog programActionLog = new ProgramActionLog();
            programActionLog.setHandle( BOHandleUtil.buildProgramActionLogHandle() );
            programActionLog.setSite( site );
            programActionLog.setMachineType( machineType );
            programActionLog.setCncType( cncType );
            programActionLog.setResrce( resource );
            programActionLog.setCncPrgmBo( BOHandleUtil.buildCncProgramHandle( site, machineType, cncType, prgmCode, revision, subSeq ) );
            programActionLog.setPrgmFile( programFile );
            programActionLog.setAction( ProgramAction.UPLOAD.toString() );
            programActionLog.setCreatedDateTime( new Date() );
            programActionLog.setCreateUserBo( CommonMethods.getUserBo() );
            programActionLogService.insertProgramActionLog( programActionLog );
        }

        String remotePath = machineType + "-" + cncType + File.separator + prgmCode + "-" + revision + "-" + subSeq;
        String remoteFile = remotePath + File.separator + programFile.substring( 0, programFile.indexOf( "." ) ) + File.separator;

        JSONObject jsonObject = new JSONObject();
        jsonObject.put( "MESSAGE_TYPE", "MES_UPLOAD_CNC_PROGRAM" );
        jsonObject.put( "RESRCE", resrce );
        jsonObject.put( "FILE_PATH", remoteFile );
        jmsTemplate.getJmsTemplate().setReceiveTimeout( 300000 );
        ActiveMQTopic topic = new ActiveMQTopic( "MES_CNC_PROGRAM" );
        String replyMessage = jmsTemplate.convertSendAndReceive( topic, jsonObject.toString(), String.class );
        if( replyMessage == null ){
            throw BusinessException.build( I18nUtil.getI18nText("production.cncProgram.uploadProgramToCncTimeout" ) );
        }
        JSONObject replyJson = JSON.parseObject( replyMessage );
        JSONArray resultArray = replyJson.getJSONArray( "RESULT" );
        StringBuilder errorMsg = new StringBuilder();
        for( int i = 0; i < resultArray.size(); i++ ){
            JSONObject replyItem = resultArray.getJSONObject( i );
            if( replyItem.getIntValue( "CODE" ) != 0 ){
                errorMsg.append( I18nUtil.getI18nText( "production.cncProgram.uploadProgramToCncFail", new String[]{ replyItem.getString( "RESRCE" ), replyJson.getString( "MESSAGE" ) } ) );
            }
        }
        if( errorMsg.length() > 0 ){
            throw BusinessException.build( errorMsg.toString() );
        }
    }

    /**
     * 从cnc下载程序并压缩
     *
     * @param site
     * @param resrce
     * @return
     */
    public File downloadProgramFromCnc( String site, String resrce ){
        /**检查站点*/
        siteAdviceService.existSite( site );
        /**检查设备编号*/
        resourceAdviceService.existResource( site, resrce );
        /**检查机种*/
        String machineType = itemGroupAdviceService.findItemGroupBySite( site );
        /**检查夹位*/
        String cncType = resourceAdviceService.findCncType( site, resrce );

        String remotePath = machineType + "-" + cncType + File.separator + machineType + "-" + cncType + "-" + resrce;
        try {
            ftpUtil.mkdirs( remotePath );
        } catch (Exception e) {
            e.printStackTrace();
        }

        /**发送下载程式指令*/
        JSONObject jsonObject = new JSONObject();
        jsonObject.put( "MESSAGE_TYPE", "MES_DOWNLOAD_CNC_PROGRAM" );
        jsonObject.put( "RESRCE", resrce );
        jsonObject.put( "FILE_PATH", remotePath + File.separator );
        jmsTemplate.getJmsTemplate().setReceiveTimeout( 180000 );
        ActiveMQTopic topic = new ActiveMQTopic( "MES_CNC_PROGRAM" );
        String replyMessage = jmsTemplate.convertSendAndReceive( topic, jsonObject.toString(), String.class );
        if( replyMessage == null ){
            throw BusinessException.build( I18nUtil.getI18nText("production.cncProgram.downloadProgramFromCncTimeout" ) );
        }
        JSONObject replyJson = JSON.parseObject( replyMessage );
        if( replyJson.getIntValue( "CODE" ) != 0 ){
            throw BusinessException.build(replyJson.getString( "MESSAGE" ));
        }

        /**保存CNC程式下载记录*/
        ProgramActionLog programActionLog = new ProgramActionLog();
        programActionLog.setHandle( BOHandleUtil.buildProgramActionLogHandle() );
        programActionLog.setSite( site );
        programActionLog.setMachineType( machineType );
        programActionLog.setCncType( cncType );
        programActionLog.setResrce( resrce );
        programActionLog.setAction( ProgramAction.DOWNLOAD.toString() );
        programActionLog.setCreatedDateTime( new Date() );
        programActionLog.setCreateUserBo( CommonMethods.getUserBo() );
        programActionLogService.insertProgramActionLog( programActionLog );

        String localPath = ftpUtil.getDownloadDir() + remotePath;
        File zipFile = new File( ftpUtil.getDownloadDir() + File.separator + machineType + "-" + cncType + "-" + resrce + ".zip" );

        try {
            ftpUtil.download( remotePath, localPath );
            ftpUtil.deleteFile( remotePath );

            ZipUtil.zip( zipFile, localPath );
        } catch (Exception e) {
            throw BusinessException.build( e.getMessage() );
        }finally {
            try {
                FileUtils.deleteDirectory( new File( localPath ) );
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return zipFile;
    }

    /**
     * 打包程式子程式清单
     *
     * @param cncProgram
     * @param cncProgramItems
     * @return
     */
    public File zipProgramItem( CncProgram cncProgram, List<CncProgramItem> cncProgramItems  ){

        String machineType = cncProgram.getMachineType();
        String cncType = cncProgram.getCncType();
        String prgmCode = cncProgram.getPrgmCode();
        String revision = cncProgram.getRevision();
        String subSeq = cncProgram.getSubSeq();
        String filePath = machineType + "-" + cncType + File.separator + prgmCode + "-" + revision + "-" + subSeq;
        String prgmFileName = prgmCode + "-" + revision + "-" + subSeq + ".zip";

        String downloadDir = ftpUtil.getDownloadDir();
        File tempFile = new File( downloadDir + File.separator + filePath );
        tempFile.mkdirs();

        File zipFile = new File( downloadDir + File.separator + prgmFileName );
        try{
            for( CncProgramItem cncProgramItem : cncProgramItems ){
                ftpUtil.download( cncProgramItem.getPrgmFile(), new File( tempFile.getPath() + File.separator + cncProgramItem.getPrgmCode() ) );
            }
            ZipUtil.zip( zipFile, tempFile.getPath() );
        } catch (Exception e) {
            throw BusinessException.build( e.getMessage() );
        }finally {
            try {
                FileUtils.deleteDirectory( tempFile.getParentFile() );
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return zipFile;
    }

    public List<Map<String,String>> buildUploadFileList( File file, String filePath ){
        List<Map<String,String>> fileList = new ArrayList<>();
        if( file.isDirectory() ){
            filePath = filePath + File.separator + file.getName();
            File[] files = file.listFiles();
            for( File f : files ){
                List<Map<String,String>> _fileList = buildUploadFileList( f, filePath );
                if( _fileList.size() > 0 ){
                    fileList.addAll( _fileList );
                }
            }
        }else {
            Map<String,String> fileMap = new HashMap<>();
            fileMap.put( "prgmCode", file.getName() );
            fileMap.put( "prgmFile", filePath + File.separator + file.getName() );
            fileList.add( fileMap );
        }
        return fileList;
    }

    /**
     * 查询机种程式
     *
     * @param site
     * @param machineType
     * @param cncType
     * @param prgmCode
     * @param revision
     * @param subSeq
     * @return
     */
    public CncProgram findCncProgram( String site, String machineType, String cncType, String prgmCode, String revision, String subSeq ){
        /**检查站点*/
        Assert.hasText( site, I18nUtil.getI18nText( "common.siteNotBlank" ) );
        /**检查夹位*/
        Assert.hasText( cncType, I18nUtil.getI18nText( "production.cutter.cncType" ) );
        /**检查机种*/
        Assert.hasText( machineType, I18nUtil.getI18nText( "common.machineTypeNotBlank" ) );

        CncProgram cncProgram = null;
        if( StringUtils.isBlank( prgmCode ) && StringUtils.isBlank( revision ) && StringUtils.isBlank( subSeq ) ){
            cncProgram = findCurrentRevCncProgram( site, machineType, cncType );
        }else{
            CncProgram queryParam = new CncProgram();
            queryParam.setSite( site );
            queryParam.setMachineType( machineType );
            queryParam.setCncType( cncType );
            queryParam.setPrgmCode( prgmCode );
            queryParam.setRevision( revision );
            queryParam.setSubSeq( subSeq );
            List<CncProgram> cncPrograms = cncProgramService.selectCncProgramList( queryParam );
            if( cncPrograms != null && cncPrograms.size() > 0 ){
                cncProgram = cncPrograms.get( 0 );
            }
        }

        return cncProgram;
    }

    /**
     * 查询当前版本机种程式
     *
     * @param site
     * @param machineType
     * @param cncType
     * @return
     */
    public CncProgram findCurrentRevCncProgram( String site, String machineType, String cncType ){
        CncProgram queryParam = new CncProgram();
        queryParam.setSite( site );
        queryParam.setMachineType( machineType );
        queryParam.setCncType( cncType );
        queryParam.setCurrentRevision( "true" );
        List<CncProgram> cncPrograms = cncProgramService.selectCncProgramList( queryParam );
        if( cncPrograms != null && cncPrograms.size() > 0 ){
            return cncPrograms.get( 0 );
        }
        return null;
    }
}
